// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1

//! \file

#ifndef SWIFT_GUI_MODELS_COLUMNFORMATTERS_H
#define SWIFT_GUI_MODELS_COLUMNFORMATTERS_H

#include <QFlags>
#include <QList>
#include <QString>
#include <Qt>
#include <QtGlobal>

#include "gui/led.h"
#include "gui/swiftguiexport.h"
#include "misc/icon.h"
#include "misc/icons.h"
#include "misc/pq/angle.h"
#include "misc/pq/frequency.h"
#include "misc/pq/length.h"
#include "misc/pq/speed.h"
#include "misc/pq/units.h"
#include "misc/variant.h"

namespace swift::gui::models
{
    //! Column formatter default implementation, also serving as interface
    class SWIFT_GUI_EXPORT CDefaultFormatter
    {
    public:
        //! Constructor
        CDefaultFormatter(int alignment = alignDefault(), bool i18n = true,
                          const QList<int> &supportedRoles = { Qt::DisplayRole })
            : m_supportedRoles(supportedRoles), m_alignment(alignment), m_useI18n(i18n)
        {}

        //! Virtual destructor
        virtual ~CDefaultFormatter() {}

        //! Copy constructor
        CDefaultFormatter(const CDefaultFormatter &) = default;

        //! Copy assignment operator
        CDefaultFormatter &operator=(const CDefaultFormatter &) = default;

        //! Flags
        virtual Qt::ItemFlags flags(Qt::ItemFlags flags, bool editable) const;

        //! Value provided as CVariant, formatter converts to standard types or string.
        //! Used with Qt::DisplayRole displaying a text.
        virtual swift::misc::CVariant displayRole(const swift::misc::CVariant &dataCVariant) const;

        //! Value provided as CVariant, formatter converts to standard types or string.
        //! Used with Qt::DisplayRole displaying a text.
        virtual swift::misc::CVariant editRole(const swift::misc::CVariant &dataCVariant) const;

        //! Value provided as CVariant, formatter converts to QString.
        //! Used with Qt::ToolTipRole displaying a text.
        virtual swift::misc::CVariant tooltipRole(const swift::misc::CVariant &value) const;

        //! Value provided as CVariant, formatted as icon (Qt docu: "The data to be rendered as a decoration in the form
        //! of an icon"). Used with Qt::DecorationRole displaying an icon, method returns pixmap, icon, or color (see
        //! docu)
        virtual swift::misc::CVariant decorationRole(const swift::misc::CVariant &dataCVariant) const;

        //! Qt::Alignment (as CVariant)
        virtual swift::misc::CVariant alignmentRole() const;

        //! Value provided as CVariant (expecting a bool), returning as Qt::CheckStae
        virtual swift::misc::CVariant checkStateRole(const swift::misc::CVariant &value) const;

        //! Alignment available?
        virtual bool hasAlignment() const { return m_alignment >= 0; }

        //! Is given role supported by formatter
        bool supportsRole(int role) const;

        //! Receives CVariant of column data, and returns CVariant wrapping string, pixmap, or other values depending on
        //! role
        virtual swift::misc::CVariant data(int role, const swift::misc::CVariant &inputData) const;

        //! Default value
        static int alignDefault();

        //! Align left/vertically centered
        static int alignLeftVCenter() { return Qt::AlignVCenter | Qt::AlignLeft; }

        //! Align left/vertically on top
        static int alignLeftTop() { return Qt::AlignTop | Qt::AlignLeft; }

        //! Align centered
        static int alignCentered() { return Qt::AlignVCenter | Qt::AlignHCenter; }

        //! Align right/vertically centered
        static int alignRightVCenter() { return Qt::AlignVCenter | Qt::AlignRight; }

        //! Display role
        static const QList<int> &roleDisplay()
        {
            static const QList<int> r({ Qt::DisplayRole });
            return r;
        }

        //! Display role
        static const QList<int> &rolesDisplayAndEdit()
        {
            static const QList<int> r({ Qt::DisplayRole, Qt::EditRole });
            return r;
        }

        //! Decoration + ToolTip role
        static const QList<int> &rolesDecorationAndToolTip()
        {
            static const QList<int> r({ Qt::DecorationRole, Qt::ToolTipRole });
            return r;
        }

        //! CheckState role
        static const QList<int> &roleCheckState()
        {
            static const QList<int> r({ Qt::CheckStateRole });
            return r;
        }

        //! No roles
        static const QList<int> &rolesNone()
        {
            static const QList<int> r;
            return r;
        }

    protected:
        //! Standard conversion
        virtual swift::misc::CVariant
        keepStandardTypesConvertToStringOtherwise(const swift::misc::CVariant &inputData) const;

        //! Empty string CVariant
        static const swift::misc::CVariant &emptyStringVariant();

        //! Empty pixmap CVariant
        static const swift::misc::CVariant &emptyPixmapVariant();

        QList<int> m_supportedRoles = roleDisplay(); //!< supports decoration roles
        int m_alignment = -1; //!< alignment horizontal/vertically / Qt::Alignment
        bool m_useI18n = true; //!< i18n?
    };

    //! Pixmap formatter
    class CPixmapFormatter : public CDefaultFormatter
    {
    public:
        //! Constructor
        CPixmapFormatter(int alignment = alignDefault(), const QList<int> &supportedRoles = rolesDecorationAndToolTip())
            : CDefaultFormatter(alignment, false, supportedRoles)
        {}

        //! \copydoc CDefaultFormatter::displayRole
        virtual swift::misc::CVariant displayRole(const swift::misc::CVariant &dataCVariant) const override;

        //! \copydoc CDefaultFormatter::tooltipRole
        virtual swift::misc::CVariant tooltipRole(const swift::misc::CVariant &dataCVariant) const override;

        //! \copydoc CDefaultFormatter::decorationRole
        virtual swift::misc::CVariant decorationRole(const swift::misc::CVariant &dataCVariant) const override;

        //! @{
        //! Width/height
        int getMaxWidth() const { return m_maxWidth; }
        int getMaxHeight() const { return m_maxHeight; }
        void setMaxWidth(int w) { m_maxWidth = w; }
        void setMaxHeight(int h) { m_maxHeight = h; }
        //! @}

    private:
        int m_maxWidth = -1;
        int m_maxHeight = -1;
    };

    //! String formatter, if known the variant already contains the appropriate string
    class CStringFormatter : public CDefaultFormatter
    {
    public:
        //! Constructor
        CStringFormatter(int alignment = alignDefault()) : CDefaultFormatter(alignment, false, roleDisplay()) {}

        //! \copydoc CDefaultFormatter::displayRole
        virtual swift::misc::CVariant displayRole(const swift::misc::CVariant &dataCVariant) const override;
    };

    //! Just returns a empty "" value
    class CEmptyFormatter : public CDefaultFormatter
    {
    public:
        //! Constructor
        CEmptyFormatter(int alignment = alignDefault()) : CDefaultFormatter(alignment, false, roleDisplay()) {}

        //! \copydoc CDefaultFormatter::displayRole
        virtual swift::misc::CVariant displayRole(const swift::misc::CVariant &dataCVariant) const override;
    };

    //! Just returns a empty "" value
    class CIncognitoFormatter : public CDefaultFormatter
    {
    public:
        //! Constructor
        CIncognitoFormatter(int alignment = alignDefault()) : CDefaultFormatter(alignment, false, roleDisplay()) {}

        //! \copydoc CDefaultFormatter::displayRole
        virtual swift::misc::CVariant displayRole(const swift::misc::CVariant &dataCVariant) const override;
    };

    //! Layout will be defined by a delegate
    class CDelegateFormatter : public CDefaultFormatter
    {
    public:
        //! Constructor
        CDelegateFormatter(int alignment = alignCentered(),
                           const QList<int> &supportedRoles = rolesDecorationAndToolTip())
            : CDefaultFormatter(alignment, false, supportedRoles)
        {}

        //! \copydoc CDefaultFormatter::flags
        virtual Qt::ItemFlags flags(Qt::ItemFlags flags, bool editable) const override;
    };

    //! Bool value, format as text
    class CBoolTextFormatter : public CDefaultFormatter
    {
    public:
        //! Constructor
        CBoolTextFormatter(int alignment = alignDefault(), const QString &trueName = "true",
                           const QString &falseName = "false", const QList<int> &supportedRoles = roleDisplay())
            : CDefaultFormatter(alignment, false, supportedRoles),
              m_trueNameVariant(swift::misc::CVariant::from(trueName)),
              m_falseNameVariant(swift::misc::CVariant::from(falseName))
        {}

        //! \copydoc CDefaultFormatter::displayRole
        virtual swift::misc::CVariant displayRole(const swift::misc::CVariant &dataCVariant) const override;

        //! \copydoc CDefaultFormatter::flags
        virtual Qt::ItemFlags flags(Qt::ItemFlags flags, bool editable) const override;

    protected:
        const swift::misc::CVariant m_trueNameVariant = "true"; //!< displayed when true
        const swift::misc::CVariant m_falseNameVariant = "false"; //!< displayed when false
    };

    //! Format as bool LED value
    class CBoolLedFormatter : public CBoolTextFormatter
    {
    public:
        //! Constructor
        CBoolLedFormatter(int alignment = alignDefault());

        //! Constructor
        CBoolLedFormatter(const QString &onName, const QString &offName, int alignment = alignDefault());

        //! \copydoc CDefaultFormatter::displayRole
        virtual swift::misc::CVariant displayRole(const swift::misc::CVariant &dataCVariant) const override;

        //! Display the LED
        virtual swift::misc::CVariant decorationRole(const swift::misc::CVariant &dataCVariant) const override;

        //! \copydoc CDefaultFormatter::tooltipRole
        virtual swift::misc::CVariant tooltipRole(const swift::misc::CVariant &dataCVariant) const override
        {
            return CBoolTextFormatter::displayRole(dataCVariant);
        }

    protected:
        swift::misc::CVariant m_pixmapOnLedVariant; //!< Pixmap used when on
        swift::misc::CVariant m_pixmapOffLedVariant; //!< Pixmap used when off

    private:
        //! Default LED
        static CLedWidget *createLedDefault()
        {
            return new CLedWidget(false, CLedWidget::Yellow, CLedWidget::Black, CLedWidget::Rounded);
        }
    };

    //! Format as bool pixmap
    class CBoolIconFormatter : public CBoolTextFormatter
    {
    public:
        //! Constructor
        CBoolIconFormatter(int alignment = alignCentered());

        //! Constructor
        CBoolIconFormatter(const QString &onName, const QString &offName, int alignment = alignCentered());

        //! Constructor
        CBoolIconFormatter(const swift::misc::CIcon &onIcon, const swift::misc::CIcon &offIcon, const QString &onName,
                           const QString &offName, int alignment = alignCentered());

        //! Constructor
        CBoolIconFormatter(swift::misc::CIcons::IconIndex onIcon, swift::misc::CIcons::IconIndex offIcon,
                           const QString &onName, const QString &offName, int alignment = alignCentered());

        //! \copydoc CDefaultFormatter::displayRole
        virtual swift::misc::CVariant displayRole(const swift::misc::CVariant &dataCVariant) const override;

        //! Display the icon
        virtual swift::misc::CVariant decorationRole(const swift::misc::CVariant &dataCVariant) const override;

        //! \copydoc CDefaultFormatter::tooltipRole
        virtual swift::misc::CVariant tooltipRole(const swift::misc::CVariant &dataCVariant) const override;

    protected:
        const swift::misc::CVariant m_iconOnVariant; //!< Used when on
        const swift::misc::CVariant m_iconOffVariant; //!< Used when off
    };

    //! Default formatter when column contains CValueObject
    class CValueObjectFormatter : public CDefaultFormatter
    {
    public:
        //! Constructor
        CValueObjectFormatter(int alignment = alignDefault(), bool i18n = true,
                              const QList<int> &supportedRoles = roleDisplay())
            : CDefaultFormatter(alignment, i18n, supportedRoles)
        {}

        //! \copydoc CDefaultFormatter::displayRole
        virtual swift::misc::CVariant displayRole(const swift::misc::CVariant &valueObject) const override;

        //! \copydoc CDefaultFormatter::decorationRole
        virtual swift::misc::CVariant decorationRole(const swift::misc::CVariant &valueObject) const override;
    };

    //! Formatter when column contains QDateTime, QDate or QTime
    class CDateTimeFormatter : public CDefaultFormatter
    {
    public:
        //! Constructor
        CDateTimeFormatter(const QString &formatString = formatYmd(), int alignment = alignDefault(), bool i18n = true);

        //! \copydoc CDefaultFormatter::displayRole
        virtual swift::misc::CVariant displayRole(const swift::misc::CVariant &dateTime) const override;

        //! Year month day
        static const QString &formatYmd()
        {
            static const QString f = "yyyy-MM-dd";
            return f;
        }

        //! Year month day hour minute
        static const QString &formatYmdhm()
        {
            static const QString f = "yyyy-MM-dd HH:mm";
            return f;
        }

        //! Hour minute
        static const QString &formatHm()
        {
            static const QString f = "HH:mm";
            return f;
        }

        //! Hour minute second
        static const QString &formatHms()
        {
            static const QString f = "HH:mm:ss";
            return f;
        }

        //! Hour minute second and milliseconds
        static const QString &formatHmsz()
        {
            static const QString f = "HH:mm:ss.zzz";
            return f;
        }

    private:
        const QString m_formatString = "yyyy-MM-dd HH:mm"; //!< how the value is displayed
    };

    //! Formatter when column contains an integer
    class CIntegerFormatter : public CDefaultFormatter
    {
    public:
        //! Constructor
        CIntegerFormatter(int alignment = alignRightVCenter()) : CDefaultFormatter(alignment, false) {}

        //! \copydoc CDefaultFormatter::displayRole
        virtual swift::misc::CVariant displayRole(const swift::misc::CVariant &expectedInteger) const override;
    };

    //! Formatter when column contains an altitude
    class CAltitudeFormatter : public CDefaultFormatter
    {
    public:
        //! Constructor
        CAltitudeFormatter(bool flightlevel = false, int alignment = alignRightVCenter(), bool i18n = true)
            : CDefaultFormatter(alignment, i18n), m_flightLevel(flightlevel)
        {}

        //! Constructor
        CAltitudeFormatter(const swift::misc::physical_quantities::CLengthUnit &unit, bool flightlevel = false,
                           int alignment = alignRightVCenter(), bool i18n = true)
            : CDefaultFormatter(alignment, i18n), m_unit(unit), m_flightLevel(flightlevel)
        {}

        //! Set the unit, normally ft/m
        void setUnit(const swift::misc::physical_quantities::CLengthUnit &unit);

        //! \copydoc CDefaultFormatter::displayRole
        virtual swift::misc::CVariant displayRole(const swift::misc::CVariant &altitude) const override;

    private:
        swift::misc::physical_quantities::CLengthUnit m_unit = swift::misc::physical_quantities::CLengthUnit::ft();
        const bool m_flightLevel = false;
    };

    //! Formatter when column contains a color
    class CColorFormatter : public CDefaultFormatter
    {
    public:
        //! Constructor
        CColorFormatter(int alignment = alignCentered(), bool i18n = true);

        //! \copydoc CDefaultFormatter::displayRole
        virtual swift::misc::CVariant displayRole(const swift::misc::CVariant &dataCVariant) const override;

        //! \copydoc CDefaultFormatter::tooltipRole
        virtual swift::misc::CVariant tooltipRole(const swift::misc::CVariant &dataCVariant) const override;

        //! Display the icon
        virtual swift::misc::CVariant decorationRole(const swift::misc::CVariant &dataCVariant) const override;
    };

    //! Formatter for physical quantities
    template <class MU, class PQ>
    class CPhysiqalQuantiyFormatter : public CValueObjectFormatter
    {
    public:
        //! Constructor
        CPhysiqalQuantiyFormatter(MU unit = MU::defaultUnit(), int digits = 2, int alignment = alignRightVCenter(),
                                  bool withUnit = true, bool i18n = true,
                                  const QList<int> &supportedRoles = roleDisplay())
            : CValueObjectFormatter(alignment, i18n, supportedRoles), m_unit(unit), m_digits(digits),
              m_withUnit(withUnit)
        {}

        //! \copydoc swift::gui::models::CDefaultFormatter::displayRole
        virtual swift::misc::CVariant displayRole(const swift::misc::CVariant &physicalQuantity) const override
        {
            if (physicalQuantity.canConvert<PQ>())
            {
                const PQ pq = physicalQuantity.value<PQ>();
                return this->displayRole(pq);
            }
            else
            {
                Q_ASSERT_X(false, "CPhysiqalQuantiyFormatter::displayRole", "No CPhysicalQuantity class");
                return emptyStringVariant();
            }
        }

        //! Version if value is already available as PQ
        swift::misc::CVariant displayRole(const PQ &pq) const
        {
            if (pq.isNull())
            {
                static const swift::misc::CVariant null("null");
                return null;
            }
            return pq.valueRoundedWithUnit(m_unit, m_digits, m_useI18n);
        }

        //! Set unit
        virtual void setUnit(const MU &unit) { m_unit = unit; }

        //! Enable unit display
        virtual void enableUnit(bool enable) { m_withUnit = enable; }

        //! Digits
        virtual void setDigits(int digits) { m_digits = digits; }

    protected:
        MU m_unit; //!< unit
        int m_digits = 2; //!< digits
        bool m_withUnit = true; //!< format with unit?
    };

    //! COM frequencies
    class CComFrequencyFormatter :
        public CPhysiqalQuantiyFormatter<swift::misc::physical_quantities::CFrequencyUnit,
                                         swift::misc::physical_quantities::CFrequency>
    {
    public:
        //! Constructor
        CComFrequencyFormatter(int alignment = alignRightVCenter(), bool withUnit = true, bool i18n = true)
            : CPhysiqalQuantiyFormatter(swift::misc::physical_quantities::CFrequencyUnit::MHz(), 3, alignment, withUnit,
                                        i18n)
        {}

        //! \copydoc CDefaultFormatter::displayRole
        virtual swift::misc::CVariant displayRole(const swift::misc::CVariant &dataCVariant) const override;
    };

    //! Angle in degrees
    class CAngleDegreeFormatter :
        public CPhysiqalQuantiyFormatter<swift::misc::physical_quantities::CAngleUnit,
                                         swift::misc::physical_quantities::CAngle>
    {
    public:
        //! Constructor
        CAngleDegreeFormatter(int alignment = alignRightVCenter(), bool withUnit = true, bool i18n = true)
            : CPhysiqalQuantiyFormatter(swift::misc::physical_quantities::CAngleUnit::deg(), 0, alignment, withUnit,
                                        i18n)
        {}
    };

    //! Latitude or Longitude formatter
    class CLatLonFormatter : public CValueObjectFormatter
    {
    public:
        //! Constructor
        CLatLonFormatter(int alignment = alignRightVCenter()) : CValueObjectFormatter(alignment) {}
    };

    //! Airspace distance displayed in NM
    class CAirspaceDistanceFormatter :
        public CPhysiqalQuantiyFormatter<swift::misc::physical_quantities::CLengthUnit,
                                         swift::misc::physical_quantities::CLength>
    {
    public:
        //! Constructor
        CAirspaceDistanceFormatter(int alignment = alignRightVCenter(), bool withUnit = true, bool i18n = true)
            : CPhysiqalQuantiyFormatter(swift::misc::physical_quantities::CLengthUnit::NM(), 1, alignment, withUnit,
                                        i18n)
        {}

        //! \copydoc CDefaultFormatter::displayRole
        virtual swift::misc::CVariant displayRole(const swift::misc::CVariant &dataCVariant) const override;
    };

    //! Speed displayed in kts
    class CSpeedKtsFormatter :
        public CPhysiqalQuantiyFormatter<swift::misc::physical_quantities::CSpeedUnit,
                                         swift::misc::physical_quantities::CSpeed>
    {
    public:
        //! Constructor
        CSpeedKtsFormatter(int alignment = alignRightVCenter(), bool withUnit = true, bool i18n = true)
            : CPhysiqalQuantiyFormatter(swift::misc::physical_quantities::CSpeedUnit::kts(), 0, alignment, withUnit,
                                        i18n)
        {}

        //! \copydoc CDefaultFormatter::displayRole
        virtual swift::misc::CVariant displayRole(const swift::misc::CVariant &dataCVariant) const override;
    };
} // namespace swift::gui::models

#endif // SWIFT_GUI_MODELS_COLUMNFORMATTERS_H
